﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Fwql.Net.Extensions
{
    public static class DirectoryInfoExtension
    {
        /// <summary>
        /// 查找并返回目录下匹配其中一个表达式的所有文件
        /// </summary>
        /// <param name = "directory">目录</param>
        /// <param name = "patterns">表达式数组</param>
        /// <returns></returns>
        /// <example>
        /// 	<code>
        /// 		var files = directory.GetFiles("*.txt", "*.xml");
        /// 	</code>
        /// </example>
        public static FileInfo[] GetFiles(this DirectoryInfo directory, params string[] patterns)
        {
            var files = new List<FileInfo>();
            foreach (var pattern in patterns)
                files.AddUnique(directory.GetFiles(pattern));
            return files.ToArray();
        }

        /// <summary>
        /// 递归查找目录及其子目录下的第一个匹配表达式的文件
        /// </summary>
        /// <param name = "directory">目录</param>
        /// <param name = "pattern">表达式</param>
        /// <returns></returns>
        /// <example>
        /// 	<code>
        /// 		var directory = new DirectoryInfo(@"c:\");
        /// 		var file = directory.FindFileRecursive("win.ini");
        /// 	</code>
        /// </example>
        public static FileInfo FindFileRecursive(this DirectoryInfo directory, string pattern)
        {
            var files = directory.GetFiles(pattern);
            if (files.Length > 0)
                return files[0];

            return
                directory.GetDirectories().Select(subDirectory => subDirectory.FindFileRecursive(pattern)).
                    FirstOrDefault(foundFile => foundFile != null);
        }

        /// <summary>
        /// 递归查找目录及其子目录下的第一个匹配表达式的文件
        /// </summary>
        /// <param name = "directory">目录</param>
        /// <param name = "predicate">谓语</param>
        /// <returns></returns>
        /// <example>
        /// 	<code>
        /// 		var directory = new DirectoryInfo(@"c:\");
        /// 		var file = directory.FindFileRecursive(f => f.Extension == ".ini");
        /// 	</code>
        /// </example>
        public static FileInfo FindFileRecursive(this DirectoryInfo directory, Func<FileInfo, bool> predicate)
        {
            foreach (var file in directory.GetFiles())
            {
                if (predicate(file))
                    return file;
            }

            return
                directory.GetDirectories().Select(subDirectory => subDirectory.FindFileRecursive(predicate)).
                    FirstOrDefault(foundFile => foundFile != null);
        }

        /// <summary>
        /// 递归查找目录及其子目录下所有匹配表达式的文件
        /// </summary>
        /// <param name = "directory">目录</param>
        /// <param name = "pattern">表达式</param>
        /// <returns></returns>
        /// <example>
        /// 	<code>
        /// 		var directory = new DirectoryInfo(@"c:\");
        /// 		var files = directory.FindFilesRecursive("*.ini");
        /// 	</code>
        /// </example>
        public static FileInfo[] FindFilesRecursive(this DirectoryInfo directory, string pattern)
        {
            var foundFiles = new List<FileInfo>();
            FindFilesRecursive(directory, pattern, foundFiles);
            return foundFiles.ToArray();
        }

        private static void FindFilesRecursive(DirectoryInfo directory, string pattern, List<FileInfo> foundFiles)
        {
            foundFiles.AddUnique(directory.GetFiles(pattern));
            directory.GetDirectories().ToList().ForEach(dir => FindFilesRecursive(dir, pattern, foundFiles));
        }

        /// <summary>
        /// 递归查找目录及其子目录下所有匹配表达式的文件
        /// </summary>
        /// <param name = "directory">目录</param>
        /// <param name = "predicate">谓语</param>
        /// <returns></returns>
        /// <example>
        /// 	<code>
        /// 		var directory = new DirectoryInfo(@"c:\");
        /// 		var files = directory.FindFilesRecursive(f => f.Extension == ".ini");
        /// 	</code>
        /// </example>
        public static FileInfo[] FindFilesRecursive(this DirectoryInfo directory, Func<FileInfo, bool> predicate)
        {
            var foundFiles = new List<FileInfo>();
            FindFilesRecursive(directory, predicate, foundFiles);
            return foundFiles.ToArray();
        }

        private static void FindFilesRecursive(DirectoryInfo directory,
                                               Func<FileInfo, bool> predicate,
                                               List<FileInfo> foundFiles)
        {
            foundFiles.AddRange(directory.GetFiles().Where(predicate));
            directory.GetDirectories().ToList().ForEach(d => FindFilesRecursive(d, predicate, foundFiles));
        }
    }
}